//
//  Evaluation.cpp
//  Dictionary
//
//  Created by Rune Laugesen on 23/08/11.
//  Copyright 2011 Copenhagen University. All rights reserved.
//

#include <iostream>
#include "Evaluation.h"


Evaluation::Evaluation()
{
    //_testdata = multimap<string, vector<string> >();
    _testdata = vector<TrainingInstance>();
    _classified = vector<string>();
    _misclassified = map<string, map<double, string> >();
}

void Evaluation::BuildTestData(vector<vector<string> > testdata, map<string, vector<string> > featuredata, SWPS swps)
{
    //Indicates first word in sentance, if offset exceeds the limit value is ""
    int newsentancecount = 1;
    TrainingInstance instance;
    
    for (int i = 0; i < testdata[0].size(); i++)
    {
        string word = testdata[0][i];
        if (word == "")
        {
            newsentancecount = 1;
        }
        else if (word == "," || word == "." || word == "?" || word == "!")
        {
            continue;
        }
        else
        {
            vector<string> features(swps.featurecount-1);
            for (int j = 1; j < swps.featurecount; j++)
            {
                //If no predecessor with that offset in sentance then add empty string
                if ((newsentancecount + swps.features[j].offset) < 1)
                {
                    features[j-1] = "";
                }
                else
                {
                    if (swps.features[j].index == 0)
                    {
                        features[j-1] = testdata[0][i + swps.features[j].offset];
                    }
                    else
                    {
                        map<string, vector<string> >::iterator fit = featuredata.find(word);
                        if (fit != featuredata.end())
                        {
                            features[j-1] = (*fit).second[j-1];
                        }
                        else
                        {
                            features[j-1] = "";
                        }
                    }
                }
            }
            instance.word = testdata[0][i];
            instance.features = features;
            //_testdata.insert(pair<string, vector<string> >(testdata[0][i], features));
            _testdata.push_back(instance);
        }
        newsentancecount++;
    }
}

// add all chars
void Evaluation::KeyStrokPerCharacter(bool verbose)
{
    double d = 0.0;
    int chars = 0;
    
    for (map<string, map<double, string> >::iterator it = _misclassified.begin(); it != _misclassified.end(); it++)
    {
        string word = (*it).first;
        map<double, string> classified = (*it).second;
        chars += word.length();
        d += word.length();
        for (map<double, string>::iterator cit = classified.begin(); cit != classified.end(); cit++)
        {
            if (word != (*cit).second)
            {
                d++;
            }
            else
            {
                break;
            }
        }
    }
    
    for (int i = 0; i < _classified.size(); i++)
    {
        d += _classified[i].length();
        chars += _classified[i].length();
    }
    
    cout << "\n";
    cout << "Key stroke per character" << "\n";
    cout << "----------------------" << "\n";
    cout << "Number of misclassified: " << _misclassified.size() << "\n";
    cout << "Number of characters: " << chars << "\n";
    cout << "Key strokes: " << d << "\n";
    cout << "Key strokes per character: " << d/chars << "\n";
    cout << "----------------------" << "\n";
    
}

void Evaluation::DisambiguatedAccuracy(Classifier* classifier, KeyMapper* keymapper, SWPS swps, bool verbose)
{
    // counter for successful matches
    int success = 0;
    int nomatch = 0;
    int multimatch = 0;
    //int progresstotal = _testdata.size();
    
    for (int i = 0; i < _testdata.size(); i++)
    {
        //loadbar(i, progresstotal);
        
        map<double, string> classified;
        map<string, vector<string> > featuredata = classifier->GetFeatureData();
        string word = "";
        TrainingInstance instance = _testdata[i];
        //word = (*it).first;
        word = instance.word;
        vector<string> features = instance.features;
        vector<string> words = keymapper->GetMappings(word);

//        if (swps.featurecount == 0)
//        {
//            classified = classifier->Classify(words, swps);
//        }
//        else
//        {
            classified = classifier->Classify(words, features);
//        }
        
        map<double, string>::iterator bestmatch = classified.begin();
        
        if (bestmatch == classified.end())
        {
            nomatch++;
        }
        
        else if (bestmatch != classified.end())
        {
            bestmatch = classified.end();
            bestmatch--;
            if (words.size() > 1)
            {
                multimatch++;
            }
            if (caseInsensitiveStringCompare(word, (*bestmatch).second))
            {
                success++;
                _classified.push_back(word);
            }
            else
            {
                _misclassified.insert(pair<string, map<double, string> >(word, classified));
                if (verbose)
                {
                    cout << "predicted first:  " << (*bestmatch).second << "   should have been:  " << word << "   score:  " << (*bestmatch).first << "\n";
                }
                auto correctword = featuredata.find(word);
                if (correctword != featuredata.end())
                {
                    cout << word;
                    vector<string> correctfeatures = (*correctword).second;
                    for (int i = 0; i < swps.featurecount-1; i++)
                    {
                        cout << ";" << correctfeatures[i];
                    }
                    cout << endl;
                }
                else
                {
                    cout << word << ";" << "unknown" << endl;
                }
            }
        }
    }
    cout << "\n";
    cout << "Disambiguated accuracy" << "\n";
    cout << "----------------------" << "\n";
    cout << "number of correct classified: " << success << "\n";
    cout << "number of classifieds: " << _testdata.size() << "\n";
    cout << "number of mulitmatch: " << multimatch << "\n";
    cout << "number of no match: " << nomatch << "\n";
    cout << "correct in percents:  " << (double)success/(double)_testdata.size()*100 << "\n";
    cout << "----------------------" << "\n";
    
    this->KeyStrokPerCharacter(verbose);
}

bool Evaluation::caseInsensitiveStringCompare(const string& str1, const string& str2)
{
    if (str1.size() != str2.size()) {
        return false;
    }
    for (string::const_iterator c1 = str1.begin(), c2 = str2.begin(); c1 != str1.end(); ++c1, ++c2) {
        if (tolower(*c1) != tolower(*c2)) {
            return false;
        }
    }
    return true;
}